package dk.pun.dums.client.view;

import java.util.Date;
import java.util.List;

import com.google.gwt.cell.client.DateCell;
import com.google.gwt.cell.client.NumberCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.uibinder.client.UiTemplate;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;

import dk.pun.dums.client.widget.AddExperienceDialog;
import dk.pun.dums.shared.character.Ability;
import dk.pun.dums.shared.character.AgeBonus;
import dk.pun.dums.shared.character.CharacterAbilities;
import dk.pun.dums.shared.character.CharacterData;
import dk.pun.dums.shared.character.Clan;
import dk.pun.dums.shared.character.ExperiencePoints;
import dk.pun.dums.shared.character.GenerationBonus;
import dk.pun.dums.shared.character.TalentType;

public class ViewCharacterViewImpl extends Composite implements ViewCharacterView {
	
	private static ViewCharacterViewUiBinder uiBinder = GWT.create(ViewCharacterViewUiBinder.class);

	@UiTemplate("ViewCharacterView.ui.xml")
	interface ViewCharacterViewUiBinder extends UiBinder<Widget, ViewCharacterViewImpl> {}

	@UiField protected transient ListBox clanListBox;
	@UiField protected transient ListBox talentTypeListBox;
	@UiField protected transient ListBox powerLevelListBox;
	@UiField protected transient Label ageBonusLabel;
	@UiField protected transient PushButton ageBonusIncreaseButton;
	@UiField protected transient Label generationBonusLabel;
	@UiField protected transient PushButton generationBonusIncreaseButton;
	@UiField protected transient Label physicalTalentLabel;
	@UiField protected transient PushButton physicalTalentIncreaseButton;
	@UiField protected transient Label mentalTalentLabel;
	@UiField protected transient PushButton mentalTalentIncreaseButton;
	@UiField protected transient Label mondaneAdvantagesLabel;
	@UiField protected transient Label ressourcesLabel;
	@UiField protected transient Label magicalTalentLabel;
	@UiField protected transient Label supernaturalAdvantagesLabel;
	@UiField protected transient Label disciplinesLabel;
	@UiField protected transient Label maxBloodLabel;
	@UiField protected transient Label bloodPerFightLabel;
	@UiField protected transient CheckBox physiqueTrainedCheckBox;
	@UiField protected transient Label physiquePointsLabel;
	@UiField protected transient CheckBox craftTrainedCheckBox;
	@UiField protected transient Label craftPointsLabel;
	@UiField protected transient CheckBox criminalityTrainedCheckBox;
	@UiField protected transient Label criminalityPointsLabel;
	@UiField protected transient CheckBox meleeFightingTrainedCheckBox;
	@UiField protected transient Label meleeFightingPointsLabel;
	@UiField protected transient CheckBox survivalNatureTrainedCheckBox;
	@UiField protected transient Label survivalNaturePointsLabel;
	@UiField protected transient CheckBox investigationTrainedCheckBox;
	@UiField protected transient Label investigationPointsLabel;
	@UiField protected transient CheckBox rangedFightingTrainedCheckBox;
	@UiField protected transient Label rangedFightingPointsLabel;
	@UiField protected transient CheckBox computerTrainedCheckBox;
	@UiField protected transient Label computerPointsLabel;
	@UiField protected transient CheckBox knowledgeTrainedCheckBox;
	@UiField protected transient Label knowledgePointsLabel;
	@UiField protected transient CheckBox medicineTrainedCheckBox;
	@UiField protected transient Label medicinePointsLabel;
	@UiField protected transient CheckBox occultTrainedCheckBox;
	@UiField protected transient Label occultPointsLabel;
	@UiField protected transient CheckBox survivalUrbanTrainedCheckBox;
	@UiField protected transient Label survivalUrbanPointsLabel;
	@UiField protected transient CellTable<ExperiencePoints> experienceTable;
	@UiField protected transient Button experienceButton;
	
	private transient Presenter presenter;
	private transient CharacterData characterData;
	private transient ListDataProvider<ExperiencePoints> listDataProvider;
	
	public ViewCharacterViewImpl() {
		super();
		initWidget(uiBinder.createAndBindUi(this));
		
		for (Clan c : Clan.values()) {
			this.clanListBox.addItem(c.getName());
		}
		for (int l=1; l<=10; l++) {
			this.powerLevelListBox.addItem(String.valueOf(l));
		}
		
		this.initExperienceTable();
	}

	private void initExperienceTable() {
		final Column<ExperiencePoints, Date> dateColumn = new Column<ExperiencePoints, Date>(new DateCell(DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_SHORT))) {
			@Override
			public Date getValue(ExperiencePoints experience) {
				return experience.getDate();
			}
		};
		this.experienceTable.addColumn(dateColumn, "Dato");
		
		final Column<ExperiencePoints, Number> pointsColumn = new Column<ExperiencePoints, Number>(new NumberCell()) {
			@Override
			public Number getValue(ExperiencePoints experience) {
				return experience.getPoints();
			}
		};
		this.experienceTable.addColumn(pointsColumn, "Point");

		final Column<ExperiencePoints, String> descriptionColumn = new Column<ExperiencePoints, String>(new TextCell()) {
			@Override
			public String getValue(ExperiencePoints experience) {
				return experience.getDescription();
			}
		};
		this.experienceTable.addColumn(descriptionColumn, "Beskrivelse");
		
		this.listDataProvider = new ListDataProvider<ExperiencePoints>();
		this.listDataProvider.addDataDisplay(this.experienceTable);
	}
	
	@Override
	public void setExperience(final List<ExperiencePoints> experiences) {
		final List<ExperiencePoints> list = this.listDataProvider.getList();
		list.clear();
		for (ExperiencePoints experience : experiences) {
			list.add(experience);
		}
	}
	
	@Override
	public void addExperience(ExperiencePoints experience) {
		this.listDataProvider.getList().add(experience);
	}
	
	@Override
	public void setPresenter(final Presenter presenter) {
		this.presenter = presenter;
	}
	
	@Override
	public Widget asWidget() {
		return this;
	}
	
	@Override
	public void setCharacterData(final CharacterData characterData) {
		this.characterData = characterData;
		this.clanListBox.setSelectedIndex(this.characterData.getClan().ordinal());
		this.updateTalentType(this.characterData.getClan());
		this.powerLevelListBox.setSelectedIndex(this.characterData.getPowerLevel()-1);
		this.updateBonuses();
		this.updateMondaneTalent();
		this.updateTrainedAbilities();
	}

	@UiHandler("clanListBox")
	public void clanChangeHandler(final ChangeEvent event) {
		final Clan clan = Clan.values()[this.clanListBox.getSelectedIndex()];
		this.characterData.setClan(clan);
		this.updateTalentType(clan);
		this.updateMondaneTalent();
		this.updateTrainedAbilities();
		if (this.presenter != null) {
			this.presenter.onCharacterDataUpdated(this.characterData);
		}
	}
	
	@UiHandler("powerLevelListBox")
	public void powerLevelChangeHandler(final ChangeEvent event) {
		final int powerLevel = this.powerLevelListBox.getSelectedIndex()+1;
		this.characterData.setPowerLevel(powerLevel);
		this.updateBonuses();
		this.updateMondaneTalent();
		if (this.presenter != null) {
			this.presenter.onCharacterDataUpdated(this.characterData);
		}
	}
	
	@UiHandler("talentTypeListBox")
	public void talentTypeChangeHandler(final ChangeEvent event) {
		final int selected = this.talentTypeListBox.getSelectedIndex();
		final TalentType talentType = TalentType.values()[selected];
		this.characterData.setTalentType(talentType);
		this.updateMondaneTalent();
		if (this.presenter != null) {
			this.presenter.onCharacterDataUpdated(this.characterData);
		}
	}
	
	@UiHandler("ageBonusIncreaseButton")
	public void ageBonusIncrease(final ClickEvent event) {
		this.characterData.increaseAgeBonus();
		this.updateBonuses();
		if (this.presenter != null) {
			this.presenter.onCharacterDataUpdated(this.characterData);
		}
	}
	
	@UiHandler("generationBonusIncreaseButton")
	public void generationBonusIncrease(final ClickEvent event) {
		this.characterData.increaseGenerationBonus();
		this.updateBonuses();
		if (this.presenter != null) {
			this.presenter.onCharacterDataUpdated(this.characterData);
		}
	}
	
	@UiHandler("physicalTalentIncreaseButton")
	public void physicalTalentsIncreaseClicked(final ClickEvent event) {
		this.characterData.increasePhysicalTalent();
		this.updateMondaneTalent();
		if (this.presenter != null) {
			this.presenter.onCharacterDataUpdated(this.characterData);
		}
	}
	
	@UiHandler("mentalTalentIncreaseButton")
	public void mentalTalentsIncreaseClicked(final ClickEvent event) {
		this.characterData.increaseMentalTalent();
		this.updateMondaneTalent();
		if (this.presenter != null) {
			this.presenter.onCharacterDataUpdated(this.characterData);
		}
	}
	
	@UiHandler("physiqueTrainedCheckBox")
	public void physiqueTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.physiqueTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.PHYSIQUE);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.PHYSIQUE);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("craftTrainedCheckBox")
	public void craftTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.craftTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.CRAFT);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.CRAFT);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("criminalityTrainedCheckBox")
	public void criminalityTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.criminalityTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.CRIMINALITY);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.CRIMINALITY);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("meleeFightingTrainedCheckBox")
	public void meleeFightingTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.meleeFightingTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.MELEE_FIGHTING);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.MELEE_FIGHTING);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("survivalNatureTrainedCheckBox")
	public void survivalNatureTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.survivalNatureTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.SURVIVAL_NATURE);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.SURVIVAL_NATURE);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("investigationTrainedCheckBox")
	public void investigationTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.investigationTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.INVESTIGATION);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.INVESTIGATION);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("rangedFightingTrainedCheckBox")
	public void rangedFightingTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.rangedFightingTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.RANGED_FIGHTING);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.RANGED_FIGHTING);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("computerTrainedCheckBox")
	public void computerTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.computerTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.COMPUTER);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.COMPUTER);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("knowledgeTrainedCheckBox")
	public void knowledgeTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.knowledgeTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.KNOWLEDGE);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.KNOWLEDGE);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("medicineTrainedCheckBox")
	public void medicineTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.medicineTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.MEDICINE);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.MEDICINE);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("occultTrainedCheckBox")
	public void occultTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.occultTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.OCCULT);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.OCCULT);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("survivalUrbanTrainedCheckBox")
	public void survivalUrbanTrainedClicked(final ClickEvent event) {
		final boolean isChecked = this.survivalUrbanTrainedCheckBox.getValue();
		if (isChecked) {
			this.characterData.getTrainedAbilities().addTrainedAbility(Ability.SURVIVAL_URBAN);
		} else {
			this.characterData.getTrainedAbilities().removeTrainedAbility(Ability.SURVIVAL_URBAN);
		}
		this.updateTrainedAbilities();
	}
	
	@UiHandler("experienceButton")
	public void addExperienceButtonClicked(final ClickEvent event) {
		AddExperienceDialog dialog = new AddExperienceDialog(this.presenter, this.characterData);
		dialog.show();
		dialog.center();
	}
	
	private void updateTalentType(final Clan clan) {
		this.talentTypeListBox.clear();
		final TalentType[] talentTypes = clan.getTalentTypes();
		for (TalentType t : talentTypes) {
			this.talentTypeListBox.addItem(t.getName());
		}
		this.talentTypeListBox.setSelectedIndex(0);
		if (talentTypes.length == 1) {
			this.talentTypeListBox.setEnabled(false);
		} else {
			this.talentTypeListBox.setEnabled(true);
		}
		this.updateTrainedAbilities();
	}

	private void updateBonuses() {
		final AgeBonus ageBonus = AgeBonus.getAgeBonus(this.characterData.getAgeBonus());
		final GenerationBonus generationBonus = GenerationBonus.getGenerationBonus(this.characterData.getGenerationBonus());

		this.ageBonusLabel.setText(String.valueOf(ageBonus.getAge()));
		this.generationBonusLabel.setText(String.valueOf(generationBonus.getGeneration()));
		if (this.characterData.canIncreaseAgeBonus()) {
			this.ageBonusIncreaseButton.setEnabled(true);
		} else {
			this.ageBonusIncreaseButton.setEnabled(false);
		}
		if (this.characterData.canIncreaseGenerationBonus()) {
			this.generationBonusIncreaseButton.setEnabled(true);
		} else {
			this.generationBonusIncreaseButton.setEnabled(false);
		}

		this.mondaneAdvantagesLabel.setText(String.valueOf(ageBonus.getMondaneAdvantages()));
		this.ressourcesLabel.setText(String.valueOf(ageBonus.getRessources()));

		this.magicalTalentLabel.setText(String.valueOf(generationBonus.getMagicalTalents()));
		this.supernaturalAdvantagesLabel.setText(String.valueOf(generationBonus.getSupernaturalAdvantages()));
		this.disciplinesLabel.setText(String.valueOf(generationBonus.getDisciplines()));
		this.maxBloodLabel.setText(String.valueOf(generationBonus.getMaxBlood()));
		this.bloodPerFightLabel.setText(String.valueOf(generationBonus.getBloodPerFight()));
	}

	private void updateMondaneTalent() {
		this.physicalTalentLabel.setText(String.valueOf(this.characterData.getPhysicalTalent()));
		this.mentalTalentLabel.setText(String.valueOf(this.characterData.getMentalTalent()));
		if (this.characterData.canIncreasePhysicalTalent()) {
			this.physicalTalentIncreaseButton.setEnabled(true);
		} else {
			this.physicalTalentIncreaseButton.setEnabled(false);
		}
		if (this.characterData.canIncreaseMentalTalent()) {
			this.mentalTalentIncreaseButton.setEnabled(true);
		} else {
			this.mentalTalentIncreaseButton.setEnabled(false);
		}
		this.updateTrainedAbilities();
	}
	
	private void updateTrainedAbilities() {
		this.setupTrainedAbility(Ability.PHYSIQUE, this.physiqueTrainedCheckBox, this.physiquePointsLabel);
		this.setupTrainedAbility(Ability.CRAFT, this.craftTrainedCheckBox, this.craftPointsLabel);
		this.setupTrainedAbility(Ability.CRIMINALITY, this.criminalityTrainedCheckBox, this.criminalityPointsLabel);
		this.setupTrainedAbility(Ability.MELEE_FIGHTING, this.meleeFightingTrainedCheckBox, this.meleeFightingPointsLabel);
		this.setupTrainedAbility(Ability.SURVIVAL_NATURE, this.survivalNatureTrainedCheckBox, this.survivalNaturePointsLabel);
		this.setupTrainedAbility(Ability.INVESTIGATION, this.investigationTrainedCheckBox, this.investigationPointsLabel);
		this.setupTrainedAbility(Ability.RANGED_FIGHTING, this.rangedFightingTrainedCheckBox, this.rangedFightingPointsLabel);
		this.setupTrainedAbility(Ability.COMPUTER, this.computerTrainedCheckBox, this.computerPointsLabel);
		this.setupTrainedAbility(Ability.KNOWLEDGE, this.knowledgeTrainedCheckBox, this.knowledgePointsLabel);
		this.setupTrainedAbility(Ability.MEDICINE, this.medicineTrainedCheckBox, this.medicinePointsLabel);
		this.setupTrainedAbility(Ability.OCCULT, this.occultTrainedCheckBox, this.occultPointsLabel);
		this.setupTrainedAbility(Ability.SURVIVAL_URBAN, this.survivalUrbanTrainedCheckBox, this.survivalUrbanPointsLabel);
	}

	private void setupTrainedAbility(final Ability ability, final CheckBox checkBox, final Label points) {
		final CharacterAbilities characterAbilities = this.characterData.getTrainedAbilities();
		final boolean isTrained = characterAbilities.isTrainedAbility(ability); 
		if (isTrained) {
			checkBox.setValue(true);
		} else {
			checkBox.setValue(false);
		}
		if ((!isTrained && characterAbilities.isAllTrained()) || characterAbilities.isClanAbility(ability)) {
			checkBox.setEnabled(false);
		} else {
			checkBox.setEnabled(true);
		}
		points.setText(String.valueOf(this.characterData.getAbilityPoints(ability)));
	}
}
